<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { DeleteOutlined, InboxOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons-vue'
import { formConfig } from '../config'
import { useUploadFile } from '@/composables/useUploadFile'
import { useFetch } from '@/composables/useFetch'
import { getUserDataset, uploadDataSetFile } from '@/api/cases'
import { isEmptyObject } from '@/utils'
import useStore from '@/stores'

const { cases } = useStore()

const caseInfo = ref()
const selectFormOptions = ref(formConfig.selectFormOptions)
const dynamicDRFormOptions = ref()
const dynamicClusterFormOptions = ref()
const selectFormRef = ref()
const cleanFormRef = ref()
const samplingFormRef = ref()
const normalizationFormRef = ref()
const DRFormRef = ref()
const clusterFormRef = ref()

const detailData = computed(() => caseInfo.value?.jsonArgs ? JSON.parse(caseInfo.value?.jsonArgs) : null)

const { error: uploadError, loading: uploadLoading, execute: uploadDataSetExecute } = useFetch((parmas: any = {}) => uploadDataSetFile(parmas))

getDataSetList()

// detail页面请求完详情数据后动态组件马上渲染了form页面，数据来不及回填渲染，需要等到下一个tick在渲染
nextTick(async () => {
  caseInfo.value = cases.caseInfo
  // await 保证动态Form处理完成后再进行数据回填操作
  await _handleDRDynamicForm()
  await _handleClusterDynamicForm()
  initForm()
})

// 处理动态表单
watch(() => DRFormRef.value?.model.method, (nVal: number) => {
  if (nVal || nVal === 0)
    _handleDRDynamicForm()
})
async function _handleDRDynamicForm() {
  const method = DRFormRef.value?.model.method
  // 一开始从详情数据里取，后面通过表单动态获取
  const DRMethod = (method || method === 0) ? method : detailData.value?.DR.method
  // 0-7 代表的是method字段映射值, 后面的字段代表需要在页面上展示的选项
  const map = new Map([
    [0, ['method', 'n_components', 'skip']],
    [1, ['method', 'n_components', 'skip']],
    [2, ['method', 'n_components', 'skip']],
    [3, ['method', 'n_components', 'skip', 'n_neigh', 'MN_ratio', 'FP_ratio', 'num_iters', 'pacmapinit']],
    [4, ['method', 'n_components', 'skip']],
    [5, ['method', 'n_components', 'skip', 'n_neigh']],
    [6, ['method', 'n_components', 'skip', 'perplexity']],
    [7, ['method', 'n_components', 'skip']],
  ])
  const needToShowProps = map.get(DRMethod)
  // 生成新的DRFormOptions
  dynamicDRFormOptions.value = formConfig.DRFormOptions.filter((item: any) => needToShowProps?.includes(item.prop))
  // 赋值当前选中的method，这里需要通过DRFormOptions的方式去赋值，DRFormRef.value?.model的方式赋值会和watch相互影响
  const methodOption = formConfig.DRFormOptions.find((item: any) => item.prop === 'method')!
  methodOption.value = DRMethod
}
watch(() => clusterFormRef.value?.model.method, (nVal: number) => {
  if (nVal || nVal === 0)
    _handleClusterDynamicForm()
})
async function _handleClusterDynamicForm() {
  const method = clusterFormRef.value?.model.method
  // 一开始从详情数据里取，后面通过表单动态获取
  const clusterMethod = (method || method === 0) ? method : detailData.value?.Cluster.method
  // 0-5 代表的是method字段映射值, 后面的字段代表需要在页面上展示的选项
  const map = new Map([
    [0, ['method', 'n_clusters', 'skip']],
    [1, ['method', 'n_clusters', 'skip']],
    [2, ['method', 'epsilon', 'minsamp1', 'skip']],
    [3, ['method', 'bandwidth1', 'skip']],
    [4, ['method', 'nclust2', 'threshold1', 'branch_factor', 'skip']],
    [5, ['method', 'minsamp1', 'skip']],
  ])
  const needToShowProps = map.get(clusterMethod)
  // 生成新的clusterFormOptions
  dynamicClusterFormOptions.value = formConfig.clusterFormOptions.filter((item: any) => needToShowProps?.includes(item.prop))
  // 赋值当前选中的method，这里需要通过clusterFormOptions的方式去赋值，clusterFormRef.value?.model的方式赋值会和watch相互影响
  // 这里的value是config里表单项的value
  const methodOption = formConfig.clusterFormOptions.find((item: any) => item.prop === 'method')!
  methodOption.value = clusterMethod
}
function initForm() {
  if (detailData.value && !isEmptyObject(detailData.value)) {
    const { dataset_uuid, Clean, Sampling, Normalization, DR, Cluster } = detailData.value

    selectFormRef.value.model.dataset_uuid = dataset_uuid
    const { clean_steps, ...item } = Clean
    const [clean_non_numeric, clean_Missing_Data, clean_outlier] = clean_steps
    cleanFormRef.value.model = {
      ...item,
      clean_non_numeric: Boolean(clean_non_numeric),
      clean_Missing_Data: Boolean(clean_Missing_Data),
      clean_outlier: Boolean(clean_outlier),
    }
    samplingFormRef.value.model = Sampling
    normalizationFormRef.value.model = Normalization
    DRFormRef.value.model = DR
    clusterFormRef.value.model = Cluster
  }
}

onBeforeUnmount(() => {
  dynamicClusterFormOptions.value = dynamicDRFormOptions.value = selectFormOptions.value = null as any
})

// 自动保存表单信息
watch([
  () => selectFormRef.value?.model,
  () => cleanFormRef.value?.model,
  () => samplingFormRef.value?.model,
  () => normalizationFormRef.value?.model,
  () => DRFormRef.value?.model,
  () => clusterFormRef.value?.model,
], () => {
  onClickSubmitForm()
}, { deep: true })

// 点击提交表单
async function onClickSubmitForm() {
  const validate1 = selectFormRef.value.validate()
  const validate2 = cleanFormRef.value.validate()
  const validate3 = samplingFormRef.value.validate()
  const validate4 = normalizationFormRef.value.validate()
  const validate5 = DRFormRef.value.validate()
  const validate6 = clusterFormRef.value.validate()
  return Promise.all([validate1(), validate2(), validate3(), validate4(), validate5(), validate6()]).then((res: any) => {
    _handleSubmitForm(res)
  }).catch(() => {})
}

// 处理表单参数
async function _handleSubmitForm(data: any) {
  // 前几步填写
  const { dataset_uuid } = data[0]
  const { clean_non_numeric, clean_outlier, clean_Missing_Data, ...item } = data[1]
  const Clean = {
    ...item,
    clean_steps: [+clean_non_numeric, +clean_Missing_Data, +clean_outlier],
    skip: false,
  }
  const Sampling = { ...data[2], skip: false }
  const Normalization = { ...data[3], skip: false }
  const DR = { ...data[4], skip: false }
  const Cluster = { ...data[5], skip: false }

  // 后几步带上默认参数跳过(后端需求需要全量参数)
  const { SOM, Contribution, Optimization, Sensitivity_analysis } = detailData.value

  // 保存表单信息供后面步骤使用
  const formInfo = { dataset_uuid, Clean, Sampling, Normalization, DR, Cluster, SOM, Contribution, Optimization, Sensitivity_analysis: handlerSensitiveData(Sensitivity_analysis) }
  cases.saveFormInfo(formInfo)

  // 更新页面步骤条
  const finishSteps = ['Preprocessing Step']
  cases.updateCaseProcess(finishSteps)
}

function handlerSensitiveData(sensitivityAnalysisData: any) {
  const [analysisStep, analysisStepVar] = handleFiveFormDRAndCluster(detailData.value)
  const sensitiveData = { ...sensitivityAnalysisData, analysis_step: analysisStep, analysis_var: analysisStepVar }

  return sensitiveData
}

function handleFiveFormDRAndCluster(detailData: any) {
  const drMethod = detailData.DR.method
  const clusterMethod = detailData.Cluster.method
  const drMap = new Map([
    [0, 'DR:PCA'],
    [1, 'DR:PCA'],
    [2, 'DR:Umap'],
    [3, 'DR:Pacmap'],
    [4, 'DR:ICA'],
    [5, 'DR:SPE'],
    [6, 'DR:t-SNE'],
    [7, 'DR:TriMap'],
  ])
  const clusterMap = new Map([
    [0, 'Cluster:kmeans'],
    [1, 'Cluster:kmeans'],
    [2, 'Cluster:DBSCAN'],
    [3, 'Cluster:meanshift'],
    [4, 'Cluster:birch'],
    [5, 'Cluster:HDBSCAN'],
  ])
  const varMap = new Map([
    ['DR:PCA', ['n_components']],
    ['DR:Umap', ['n_components']],
    ['DR:Pacmap', ['n_components', 'n_heigh', 'MN_ratio', 'FP_ratio', 'num_iters']],
    ['DR:ICA', ['n_components']],
    ['DR:SPE', ['n_components', 'n_neigh']],
    ['DR:t-SNE', ['n_components', 'perplexity']],
    ['DR:TriMap', ['n_components']],
    ['Cluster:kmeans', ['Cluster_numbers']],
    ['Cluster:DBSCAN', ['minsamp1', 'epsilon']],
    ['Cluster:meanshift', ['Cluster_numbers', 'bandwidth1']],
    ['Cluster:birch', ['nclust2', 'threshold1', 'branch_factor']],
    ['Cluster:HDBSCAN', ['minsamp2']],
  ])
  const step = drMap.get(drMethod) || clusterMap.get(clusterMethod) || 'DR:PCA'
  return [step, varMap.get(step)![0] || 'n_components']
}

// 获取数据集
async function getDataSetList() {
  const dataSet = await getUserDataset()
  selectFormOptions.value[0].children = dataSet.map((item: any) => {
    return {
      type: 'select-option',
      value: item,
      label: item,
    }
  })
  selectFormOptions.value[0].value = selectFormRef.value.model.dataset_uuid || dataSet[0]
}

const { fileList, beforeUpload, handleRemove } = useUploadFile()
const open = ref<boolean>(false)
const showResult = ref(false)

const title = ref('Successfully Upload Dataset File!')
const subTitle = ref('You can select the latest dataset as your operation object!')
async function onClickUploadFile() {
  if (fileList.value.length === 0)
    return

  const formData = new FormData()
  fileList.value.forEach((file) => {
    formData.append('file', file)
  })

  await uploadDataSetExecute(formData)

  if (uploadError.value) {
    title.value = 'Errorly Upload Dataset File!'
    subTitle.value = 'Please check the file format!'
  }
  showResult.value = true
}

async function onClickBackListToFocus() {
  open.value = false
  showResult.value = false
  fileList.value = []
  await getDataSetList()
  // console.log(document.getElementById('form_item_dataset_uuid'))
}
async function onClickGetNewDataSet() {
  showResult.value = false
  fileList.value = []
  await getDataSetList()
}

defineExpose({ onClickSubmitForm })
</script>

<template>
  <div class="case-form-wrapper">
    <a-descriptions bordered style="background-color: #fff; border-radius: 8px 8px 0 0; margin-bottom: 20px;">
      <template #title>
        <div class="header">
          <span>Preprocessing</span>
        </div>
      </template>
      <a-descriptions-item label="Case Name">
        {{ caseInfo?.name }}
      </a-descriptions-item>
      <a-descriptions-item label="Case Status">
        {{ caseInfo?.status }}
      </a-descriptions-item>
      <a-descriptions-item label="Update Time">
        {{ caseInfo?.lastUpdateTime }}
      </a-descriptions-item>
      <a-descriptions-item label="Disabled">
        <a-switch :checked="caseInfo?.disabled === '1'" disabled />
      </a-descriptions-item>
      <a-descriptions-item label="Shared">
        <a-switch :checked="caseInfo?.shared === '1'" disabled />
      </a-descriptions-item>
      <a-descriptions-item label="Remark">
        {{ caseInfo?.remark }}
      </a-descriptions-item>
    </a-descriptions>
    <a-card title="Select">
      <GpForm ref="selectFormRef" :options="selectFormOptions" style="width: 70%" />
      <div>
        <span style="display: inline-block; width: 100px; margin: 0 2px 0px 8px;">
          Upload DataSet:
        </span>
        <a-button style="min-width: 150px;" @click="open = true">
          <PlusOutlined /> Upload
        </a-button>
        <a-drawer
          v-model:open="open"
          class="drawer-upload-class"
          title="Upload DataSet"
          placement="right"
          :mask-closable="false"
          width="500"
          @close="onClickGetNewDataSet"
        >
          <transition name="fade-transform" mode="out-in">
            <div v-if="showResult">
              <a-result
                :status="uploadError ? 'error' : 'success'"
                :title="title"
                :sub-title="subTitle"
              >
                <template #extra>
                  <a-button key="console" type="primary" @click="onClickBackListToFocus">
                    Back List
                  </a-button>
                  <a-button key="buy" @click="onClickGetNewDataSet">
                    Upload Again
                  </a-button>
                </template>
              </a-result>
            </div>
            <div v-else>
              <div class="upload-container">
                <a-upload-dragger
                  :before-upload="beforeUpload"
                  :file-list="fileList"
                  :remove="handleRemove"
                >
                  <p class="ant-upload-drag-icon">
                    <InboxOutlined />
                  </p>
                  <p class="ant-upload-text">
                    Click or drag file to this area to upload
                  </p>
                  <p class="ant-upload-hint">
                    Support for a single upload file with extensions csv or xlsx.
                  </p>
                </a-upload-dragger>
              </div>
              <div style="margin-top: 20px;" class="gp__grid-half">
                <a-button type="primary" :loading="uploadLoading" @click="onClickUploadFile">
                  <UploadOutlined /> Upload
                </a-button>
                <a-button @click="fileList = []">
                  <DeleteOutlined /> Delete
                </a-button>
              </div>
            </div>
          </transition>
        </a-drawer>
      </div>
    </a-card>
    <a-card title="Clean">
      <GpForm ref="cleanFormRef" class="gp__grid-half" :options="formConfig.cleanFormOptions" v-bind="formConfig.baseFormLayout" />
    </a-card>
    <a-card title="Sampling">
      <GpForm ref="samplingFormRef" class="gp__grid-half" :options="formConfig.samplingFormOptions" v-bind="formConfig.baseFormLayout" />
    </a-card>
    <a-card title="Normalization">
      <GpForm
        ref="normalizationFormRef" class="gp__grid-half" :options="formConfig.normalizationFormOptions" v-bind="formConfig.baseFormLayout"
      />
    </a-card>
    <a-card title="DR">
      <GpForm ref="DRFormRef" class="gp__grid-half" :options="dynamicDRFormOptions" v-bind="formConfig.baseFormLayout" />
    </a-card>
    <a-card title="Cluster">
      <GpForm ref="clusterFormRef" class="gp__grid-half" :options="dynamicClusterFormOptions" v-bind="formConfig.baseFormLayout" />
    </a-card>
  </div>
</template>

<style lang="less" scoped>
@import url('../common.less');
</style>
